/****************************************************************************
 * Copyright (C) 2013 Dj_Skual
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************/
#include "DevSaveTask.hpp"
#include "../../FileOperations/Nand.hpp"
#include "../../FileOperations/fileops.h"
#include "../../FileOperations/DirList.h"
#include "../../Saves/SaveTools.h"
#include "../../Menu/Explorer.h"

DevSaveTask::DevSaveTask(short Type)
	: type(Type)
	, canceled(false)
	, window(NULL)
{
	ThreadedTaskHandler::Instance()->AddTask(this);
}

void DevSaveTask::OnCancelClick()
{
	canceled = true;
}

void DevSaveTask::Execute(void)
{
	if(type == INSTALL_SAVE_TO_NAND || type == INSTALL_SAVE_TO_ENAND)
	{
		window = new ManageProgressWindow(tr("Installing..."));
		window->SetCancel(false);
	}
	else if(type == DELETE_SAVE_FROM_DEV)
		window = new ManageProgressWindow(tr("Deleting..."));
	else if(type == MOVE_SAVE_TO_DEV)
		window = new ManageProgressWindow(tr("Moving..."));
	else if(type == COPY_SAVE_TO_DEV)
		window = new ManageProgressWindow(tr("Copying..."));
	
	if(type != INSTALL_SAVE_TO_NAND && type != INSTALL_SAVE_TO_ENAND)
		window->Canceled.connect(this, &DevSaveTask::OnCancelClick);
	
	int result = -1;
	if(type == DELETE_SAVE_FROM_DEV)
	{
		if(RemoveDirectory(CurrentBrowser()->GetCurrentSelectedFilepath()) > 0)
			result = 1;
	}
	else if(type == MOVE_SAVE_TO_DEV)
	{
		if(MoveDirectory(CurrentBrowser()->GetCurrentSelectedFilepath().c_str(), Settings.TmpPath.c_str()) > 0)
			result = 1;
	}
	else if(type == COPY_SAVE_TO_DEV)
	{
		if(CopyDirectory(CurrentBrowser()->GetCurrentSelectedFilepath().c_str(), Settings.TmpPath.c_str()) > 0)
			result = 1;
	}
	else if(type == INSTALL_SAVE_TO_ENAND)
	{
		result = InstallDevSaveToENand();
	}
	else if(type == INSTALL_SAVE_TO_NAND)
	{
		result = InstallDevSaveToNand();
	}
	
	delete window;
	
	if(canceled)
		result = 0;
	
	End(this, result);
}

int DevSaveTask::InstallDevSaveToENand()
{
	u64 tid = SaveTools::StrToHex64(CurrentBrowser()->GetItemName(CurrentBrowser()->GetSelIndex()));
	std::string devpath = CurrentBrowser()->GetCurrentSelectedFilepath();
	std::string emulPath = fmt("%s/title/%08x/%08x/data", Settings.TmpPath.c_str(), TITLE_UPPER(tid), TITLE_LOWER(tid));
	
	int result = -1;
	
	DirList * dirList = new DirList(devpath, ".sgmgx_index", DirList::Files | DirList::CheckSubfolders);
	if(dirList)
	{
		for(int i = 0; i < dirList->GetFilecount(); i++)
			RemoveFile(dirList->GetFilepath(i));
		
		delete dirList;
	}
	
	dirList = new DirList(devpath, NULL, DirList::Files | DirList::Dirs | DirList::CheckSubfolders);
	if(dirList)
	{
		RemoveDirectory(emulPath);
		
		result = 1;
		for(int i = 0; (i < dirList->GetFilecount()) && (result > 0); i++)
		{
			if(!dirList->IsDir(i) && strcasecmp(dirList->GetFilename(i), "index.sgmgx"))
			{
				std::string tmp_emul = dirList->GetFilepath(i);
				tmp_emul.erase(0, devpath.size());
				tmp_emul.insert(0, emulPath);
				
				CreateSubfolder(GetParentDir(tmp_emul));
				result = CopyFile(dirList->GetFilepath(i), tmp_emul.c_str());
			}
			else if(!strcasecmp(dirList->GetFilename(i), "index.sgmgx"))
			{
				RemoveFile(dirList->GetFilepath(i));
			}
		}
		
		delete dirList;
	}
	
	if (result > 0)
		result = 1;
	else
		result = -1;
	
	return result;
}

int DevSaveTask::InstallDevSaveToNand()
{
	u64 tid = SaveTools::StrToHex64(CurrentBrowser()->GetItemName(CurrentBrowser()->GetSelIndex()));
	std::string devpath = CurrentBrowser()->GetCurrentSelectedFilepath();
	std::string nandPath = SaveTools::GetNandPath(tid);
	
	CreatePathsList(devpath, nandPath);
	DeleteUselessNandFiles(nandPath);
	CreateMissingNandFiles();
	
	int result = 1;
	for(u32 i = 0; (i < isfsPath.size()) && (result == 1); i++)
	{
		result = -1;
		
		if(!CheckFile(devicePath.at(i)))
			SaveTools::RestoreSaveFile(devicePath.at(i));
		
		ByteArray buffer;
		if(LoadFileToMem(devicePath.at(i), buffer) > 0)
		{
			if(Nand::Write(isfsPath.at(i), buffer) > 0)
				result = 1;
		}
	}
	
	return result;
}

void DevSaveTask::CreatePathsList(std::string devPath, std::string nandPath)
{
	isfsPath.clear();
	devicePath.clear();
	
	DirList * dirList = new DirList(devPath, ".sgmgx_index", DirList::Files | DirList::CheckSubfolders);
	if(dirList)
	{
		for(int i = 0; i < dirList->GetFilecount(); i++)
			RemoveFile(dirList->GetFilepath(i));
		
		delete dirList;
	}
	
	dirList = new DirList(devPath, NULL, DirList::Files | DirList::Dirs | DirList::CheckSubfolders);
	if(dirList)
	{
		for(int i = 0; i < dirList->GetFilecount(); i++)
		{
			if(!dirList->IsDir(i) && strcasecmp(dirList->GetFilename(i), "index.sgmgx"))
			{
				std::string tmpName = dirList->GetFilepath(i);
				tmpName.erase(0, devPath.size());
				tmpName = FixFileName(tmpName, FIX_RESTORE);
				std::string tmpPath = devPath;
				tmpPath += tmpName;
				devicePath.push_back(tmpPath);
				
				std::string tmpNand = dirList->GetFilepath(i);
				tmpNand.erase(0, devPath.size());
				tmpNand.insert(0, nandPath);
				tmpNand = FixFileName(tmpNand, FIX_INSTALL);
				
				isfsPath.push_back(tmpNand);
			}
			else if(!strcasecmp(dirList->GetFilename(i), "index.sgmgx"))
			{
				RemoveFile(dirList->GetFilepath(i));
			}
		}
		delete dirList;
	}
}

void DevSaveTask::DeleteUselessNandFiles(std::string path)
{
	std::vector<std::string> List;
	if( Nand::ReadDir(path, List) == ISFS_OK )
	{
		for (int i = 0; i < (int)List.size(); i++)
		{
			std::string nandPath = path;
			if(nandPath[nandPath.size()-1] != '/')
				nandPath += "/";
			nandPath += List.at(i);
			
			int fd = Nand::OpenRead(nandPath);
			if(fd < 0)
				DeleteUselessNandFiles(nandPath);
			else
			{
				Nand::Close(fd);
				bool need_file = false;
				for(u32 cur = 0; (cur < isfsPath.size()) && (!need_file); cur++)
				{
					if(!nandPath.compare(isfsPath.at(cur)))
						need_file = true;
				}
				if(!need_file)
					Nand::Delete(nandPath);
			}
		}
	}
}

void DevSaveTask::CreateMissingNandFiles()
{
	for(u32 i = 0; i < isfsPath.size(); i++)
	{
		int fd = Nand::OpenRead(isfsPath.at(i));
		if(fd < 0)
			Nand::CreateFile(isfsPath.at(i), 0, 3, 3, 3);
		else
			Nand::Close(fd);
	}
}

std::string DevSaveTask::FixFileName(std::string filename, short mode)
{
	if(mode == FIX_RESTORE)
	{
		filename.assign(StringReplace(filename, "(COLON)", "&cl;"));
		filename.assign(StringReplace(filename, "(STAR)", "&st;"));
		filename.assign(StringReplace(filename, "(QUOTE)", "&qt;"));
		filename.assign(StringReplace(filename, "(INTER)", "&qm;"));
		filename.assign(StringReplace(filename, "(VERTICAL)", "&vb;"));
		filename.assign(StringReplace(filename, "(INF)", "&lt;"));
		filename.assign(StringReplace(filename, "(SUP)", "&gt;"));
	}
	else //! FIX_INSTALL
	{
		filename.assign(StringReplace(filename, "&cl;", ":"));
		filename.assign(StringReplace(filename, "&st;", "*"));
		filename.assign(StringReplace(filename, "&qt;", "\""));
		filename.assign(StringReplace(filename, "&qm;", "?"));
		filename.assign(StringReplace(filename, "&vb;", "|"));
		filename.assign(StringReplace(filename, "&lt;", "<"));
		filename.assign(StringReplace(filename, "&gt;", ">"));
	}
	
	return filename;
}
